# coding:utf-8
'''
Created on 2010-1-20

@author: Jason Green
@author-email: guileen@gmail.com
'''
from django.db import models
import re

class TreeModelManager(models.Manager):

    def forefathers(self, tree_code, root_code=None, root_object=None):
        '''
        如果指定了root_code,root_object 将从树节点上获取对象
        @param tree_code:
        @param root_code:
        @param root_object:
        '''
        codes = [tree_code, ]
        while(True):
            i = tree_code.rfind('.')
            if i <= 0:
                break
            tree_code = tree_code[:i]
            codes.append(tree_code)
        if(root_code and root_object):
            tree_dict = self.retrieve_dict(root_code, root_object)
            return (tree_dict.get(code) for code in codes)
        return self.get_query_set().filter(tree_code__in=codes).order_by('tree_code')

    def retrieve_dict(self, root_code, root_object):
        '''
        build the tree of root_object,_tree_dict attribut
        可以重新获取到树节点，避免树节点丢失
        #TODO: 无法缓存，需要缓存
        @param root_code:
        @param root_object:
        '''
        if not hasattr(root_object, '_tree_dict'):
            print 'retrieving dict...'
            tree_dict = {}
            tree_dict[root_code] = root_object
            root_object._children_list = []
            elements = self.get_query_set().filter(tree_code__startswith=root_code + '.')
            for e in elements :
                tree_dict[e.tree_code] = e
                e._children_list = []

            for e in elements:
                pcode = e.tree_code[:e.tree_code.rindex('.')]
                p = tree_dict[pcode]
                if p:
                    p._children_list.append(e)
                else:
                    raise Exception('cant find parent node of <%s>' % e.tree_code)
            root_object._tree_dict = tree_dict
        return root_object._tree_dict

    def build_tree(self, root_code, root_object):
        '''
        build the tree of root_object,_children_list attribut,return count
        @param root_code:
        @param root_object:
        '''
        return len(self.retrieve_dict(root_code, root_object))

FORE_FATHER_RE = re.compile(r'^([^\.]*\.[^\.]*)\..*')
class TreeModel(models.Model):
    '''
    一个高性能查询的树形数据结构
    TODO: 兄弟节点
    '''
    parent = models.ForeignKey('self', related_name="child_set", null=True, blank=True)
    sort_index = models.PositiveIntegerField(default=0, editable=False)
    tree_code = models.CharField(max_length=255, unique=True, editable=False)

    tree_manager = TreeModelManager()
    objects = tree_manager
    class Meta:
        abstract = True
        ordering = ('tree_code',)

    def _save_no_recursive(self, *args, **kwargs):
        #auto tree_code
        code_changed = False
        is_update = True
        if not self.pk :
            is_update = False
        if not self.sort_index :
            max_index = self._get_root_filter().filter(parent=self.parent).aggregate(max_index=models.Max('sort_index'))['max_index'] or 0
            self.sort_index = max_index + 1
        old_code = self.tree_code

        new_code = "%s.%d" % (self.parent and self.parent.tree_code or self._get_root_code(), self.sort_index,)

        if(self.tree_code != new_code):
            code_changed = True
            self.tree_code = new_code

        return (super(TreeModel, self).save(*args, **kwargs) , (is_update and code_changed), old_code)

    def _get_root_code(self):
        '''
        使子类可以使用不同的根节点
        '''
        return 't'

    def _get_root_filter(self):
        '''
        使子类可以使用不同的根节点，返回同一根节点下的查询
        '''
        return self._meta.objects.all()

    def _get_root_object(self):
        '''
        获得根节点对象
        '''
        return object()

    def save(self , *args, **kwargs):
        (r, need_recursive, old_code) = self._save_no_recursive(*args, **kwargs)
        if need_recursive:
            #TODO: 使用字符串替换方法单条update语句来解决
            # update tm set tm.tree_code=replace(tm.tree_code,old_code,new_code) 
            # from TreeCode tm 
            # where tm.tree_code like 'old_code%'

            for c in self.__class__.objects.filter(tree_code__startswith=old_code):
                c._save_no_recursive()
        return r

    def forefathers(self):
        return self.__class__.tree_manager.forefathers(self.tree_code)

    def move_postion(self, another, move_type):
        if(move_type == 'inside'):
            if self.parent == another or another.tree_code.startswith(self.tree_code):
                return
            self.parent = another
            sub_menus = self._get_root_filter().filter(parent=another)
            self.sort_index = len(sub_menus) > 0 and sub_menus[len(sub_menus) - 1].sort_index + 1 or 1
            self.save()
        else:
            self.parent = another.parent
            if(move_type == 'after'):
                sort_index = another.sort_index + 1
            elif(move_type == 'before'):
                sort_index = another.sort_index
            temps = self.__class__.objects.filter(parent=another.parent, sort_index__gte=sort_index)
            i = sort_index + 1
            for m in temps:
                m.sort_index = i
                i = i + 1
                m.save()
            self.sort_index = sort_index
            self.save()

    def move_in(self, another):
        '''
        移进某对象内部
        @param another: 可以为空
        '''
        self.move_postion(another, "inside")

    def insert(self, another):
        '''
        移至another前面
        @param another:
        '''
        self.move_postion(another, "before")

    def append(self, another):
        '''
        移至another后面
        @param another:
        '''
        self.move_postion(another, "after")

    def children_nodes(self):
        if not hasattr(self, '_children_list'):
            self.__class__.objects.build_tree(self.tree_code, self)
        return self._children_list

    def deep_child_set(self):
        return self.__class__.objects.filter(tree_code__startswith=self.tree_code)

    def build_root_tree(self):
        self.__class__.tree_manager.build_tree(self._get_root_code(), self._get_root_object())

